/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package org.osdtsystem.dataset;

import java.util.AbstractCollection;
import java.util.ArrayList;
import java.util.List;
import java.util.NoSuchElementException;
import org.osdtsystem.utils.Storage;

/**
 * An implementation of a data set using lists.
 * @author Matthias Buch-Kromann and Martin Haulrich
 */
public class DatasetRaw extends AbstractCollection<Integer> implements Dataset {
    /**
     * The data points in the data set. Deleted data points are replaced with null.
     */
    final List<DataPoint> dataPoints;
    final int dimensionality;

    /**
     * Set of dataIDs that are deleted from the data set.
     */
    List<Integer> removedIDs;

    /**
     * The storage factory used to create new objects.
     */
    Storage storage;

    /**
     * Total weight
     */
    double totalWeight = 0;

    /**
     * Constructor for the data set with the given storage engine.
     * @param dimensionality the dimensionality of the data points
     * @param storage the storage engine
     */
    public DatasetRaw(int dimensionality, Storage storage) {
        this.storage = storage;
        this.dimensionality = dimensionality;
        this.dataPoints = storage.<DataPoint>newList();
    }

    @Override
    public DatasetRaw emptyDataset() {
        return new DatasetRaw(dimensionality, storage);
    }

    @Override
    public int dimensionality() {
        return dimensionality;
    }

    @Override
    public int append(DataPoint dataPoint) {
        // Check dimensionality of data point
        if (dataPoint.dimensions() != dimensionality())
            throw new IllegalArgumentException("Tried to append data point with dimensionality "
                    + dataPoint.dimensions() + " to dataset with dimensionality "
                    + dimensionality());

        // Add data point
        dataPoints.add(dataPoint);
        totalWeight += dataPoint.weight();
        return dataPoints.size() - 1;
    }

    @Override
    public void removeID(int identifier) {
        // Create removed list if necessary
        if (removedIDs == null)
            removedIDs = storage.<Integer>newList();

        DataPoint dataPoint = dataPoints.get(identifier);
        if (dataPoint != null) {
            // Remove data point from data set
            dataPoints.set(identifier, null);
            removedIDs.add(identifier);
            totalWeight -= dataPoint.weight();
        }
    }

    @Override
    public int size() {
        return removedIDs == null
                ? dataPoints.size() : dataPoints.size() - removedIDs.size();
    }

    @Override
    public double weight() {
        return totalWeight;
    }

    @Override
    public DataPoint dataPoint(int identifier) {
        return dataPoints.get(identifier);
    }

    @Override
    public DatasetIterator iterator() {
        return new DataSetEngineIterator();
    }

    @Override
    public void trimToSize() {
        if (dataPoints instanceof ArrayList)
            ((ArrayList) dataPoints).trimToSize();
        if (removedIDs instanceof ArrayList)
            ((ArrayList) removedIDs).trimToSize();
    }

    @Override
    public DatasetRaw baseDataSet() {
        return this;
    }


    @Override
    public Dataset intersection(Dataset otherDataSet) {
        return DatasetSubset.intersection(this, otherDataSet);
    }

    @Override
    public Dataset difference(Dataset otherDataSet) {
        return DatasetSubset.difference(this, otherDataSet);
    }

    @Override
    public Dataset union(Dataset otherDataSet) {
        return DatasetSubset.union(this, otherDataSet);
    }

    public class DataSetEngineIterator implements DatasetIterator {
        int next = 0;
        int livingPredecessors = 0;

        @Override
        public boolean hasNext() {
            return livingPredecessors < size();
        }

        @Override
        public Integer next() {
            // Find next position with non-null data point
            while (dataPoints != null && next < dataPoints.size()) {
                if (dataPoints.get(next) != null) {
                    ++livingPredecessors;
                    return next++;
                } else {
                    next++;
                }
            }

            // Throw exception if no next element exists
            throw new NoSuchElementException("Cannot iterate beyond position " 
                + next + " with " + livingPredecessors + " living predecessors");
        }

        @Override
        public void remove() {
            // Check whether there is an element that we can remove
            if (next < 0 || dataPoints.get(next - 1) == null)
                throw new IllegalStateException("No element returned by next yet, " +
                    "or it has been deleted before");

            // Remove element
            removeID(next + 1);
        }

        @Override
        public void skipUntil(int threshold) {
            while (hasNext() && next < threshold) {
                next();
            }
        }
    }
}
